/*
 * Caller.java June 2007
 *
 * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package wx.xml.simpleframework.xml.core;

/**
 * The <code>Caller</code> acts as a means for the schema to invoke
 * the callback methods on an object. This ensures that the correct
 * method is invoked within the schema class. If the annotated method
 * accepts a map then this will provide that map to the method. This
 * also ensures that if specific annotation is not present in the
 * class that no action is taken on a persister callback.
 *
 * @author Niall Gallagher
 */
class Caller {

    /**
     * This is the pointer to the schema class commit function.
     */
    private final Function commit;

    /**
     * This is the pointer to the schema class validation function.
     */
    private final Function validate;

    /**
     * This is the pointer to the schema class persist function.
     */
    private final Function persist;

    /**
     * This is the pointer to the schema class complete function.
     */
    private final Function complete;

    /**
     * This is the pointer to the schema class replace function.
     */
    private final Function replace;

    /**
     * This is the pointer to the schema class resolve function.
     */
    private final Function resolve;

    /**
     * This is the context that is used to invoke the functions.
     */
    private final Context context;

    /**
     * Constructor for the <code>Caller</code> object. This is used
     * to wrap the schema class such that callbacks from the persister
     * can be dealt with in a seamless manner. This ensures that the
     * correct function and arguments are provided to the functions.
     * element and attribute XML annotations scanned from
     *
     * @param schema  this is the scanner that contains the functions
     * @param context this is the context used to acquire the session
     */
    public Caller(Scanner schema, Context context) {
        this.validate = schema.getValidate();
        this.complete = schema.getComplete();
        this.replace = schema.getReplace();
        this.resolve = schema.getResolve();
        this.persist = schema.getPersist();
        this.commit = schema.getCommit();
        this.context = context;
    }

    /**
     * This is used to replace the deserialized object with another
     * instance, perhaps of a different type. This is useful when an
     * XML schema class acts as a reference to another XML document
     * which needs to be loaded externally to create an object of
     * a different type.
     *
     * @param source the source object to invoke the function on
     * @return this returns the object that acts as the replacement
     * @throws Exception if the replacement function cannot complete
     */
    public Object replace(Object source) throws Exception {
        if (replace != null) {
            return replace.call(context, source);
        }
        return source;
    }

    /**
     * This is used to replace the deserialized object with another
     * instance, perhaps of a different type. This is useful when an
     * XML schema class acts as a reference to another XML document
     * which needs to be loaded externally to create an object of
     * a different type.
     *
     * @param source the source object to invoke the function on
     * @return this returns the object that acts as the replacement
     * @throws Exception if the replacement function cannot complete
     */
    public Object resolve(Object source) throws Exception {
        if (resolve != null) {
            return resolve.call(context, source);
        }
        return source;
    }

    /**
     * This method is used to invoke the provided objects commit function
     * during the deserialization process. The commit function must be
     * marked with the <code>Commit</code> annotation so that when the
     * object is deserialized the persister has a chance to invoke the
     * function so that the object can build further data structures.
     *
     * @param source this is the object that has just been deserialized
     * @throws Exception thrown if the commit process cannot complete
     */
    public void commit(Object source) throws Exception {
        if (commit != null) {
            commit.call(context, source);
        }
    }

    /**
     * This method is used to invoke the provided objects validation
     * function during the deserialization process. The validation function
     * must be marked with the <code>Validate</code> annotation so that
     * when the object is deserialized the persister has a chance to
     * invoke that function so that object can validate its field values.
     *
     * @param source this is the object that has just been deserialized
     * @throws Exception thrown if the validation process failed
     */
    public void validate(Object source) throws Exception {
        if (validate != null) {
            validate.call(context, source);
        }
    }

    /**
     * This method is used to invoke the provided objects persistence
     * function. This is invoked during the serialization process to
     * get the object a chance to perform an necessary preparation
     * before the serialization of the object proceeds. The persist
     * function must be marked with the <code>Persist</code> annotation.
     *
     * @param source the object that is about to be serialized
     * @throws Exception thrown if the object cannot be persisted
     */
    public void persist(Object source) throws Exception {
        if (persist != null) {
            persist.call(context, source);
        }
    }

    /**
     * This method is used to invoke the provided objects completion
     * function. This is invoked after the serialization process has
     * completed and gives the object a chance to restore its state
     * if the persist function required some alteration or locking.
     * This is marked with the <code>Complete</code> annotation.
     *
     * @param source this is the object that has been serialized
     * @throws Exception thrown if the object cannot complete
     */
    public void complete(Object source) throws Exception {
        if (complete != null) {
            complete.call(context, source);
        }
    }
}
